#include "server.h"
#include <stdio.h>
//#include <unistd.h>
#include <errno.h>
#include <modbus.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include "modbus-private.h"

// clang-format off
#ifdef _WIN32
# include <winsock2.h>
#else
# include <sys/socket.h>
#endif
#include <unistd.h>

#if defined(_WIN32)
#include <ws2tcpip.h>
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#endif

#define NB_CONNECTION 5
using namespace std;

#define DF_LOOP_TM_MS 1

int server_tcp::init()
{
    std::thread t = std::thread([=](){
        this->process();
    });
    t.detach();
}

int server_tcp::process1()
{
    int s = -1;
    modbus_t *ctx;
    modbus_mapping_t *mb_mapping;

    ctx = modbus_new_tcp("192.168.0.81", 1502);
    printf("tcp listen port:1502\n");
    /* modbus_set_debug(ctx, TRUE); */

    //mb_mapping = modbus_mapping_new(500, 500, 500, 500);
    /*mb_mapping = mbserver_mgr::getInstance()->get_regmap();
    if (mb_mapping == NULL) {
        fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno));
        modbus_free(ctx);
        return -1;
    }*/

    s = modbus_tcp_listen(ctx, 1);
    modbus_tcp_accept(ctx, &s);
    printf("accpected a client\n");
    for (;;) {
        uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
        int rc;

        rc = modbus_receive(ctx, query);
        if (rc > 0) {
            /* rc is the query size */
            unsigned int  offset = ctx->backend->header_length;
            int slaveid = query[offset-1];
            mb_mapping = mbserver_mgr::getInstance()->get_regmap(slaveid);
            if (mb_mapping == NULL) {
                fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno));
                //modbus_free(ctx);
                //return -1;
                continue;
            }

            modbus_reply(ctx, query, rc, mb_mapping);
        } else if (rc == -1) {
            /* Connection closed by the client or error */
            break;
        }
    }

    printf("Quit the loop: %s\n", modbus_strerror(errno));

    if (s != -1) {
        close(s);
    }
    //modbus_mapping_free(mb_mapping);
    modbus_close(ctx);
    modbus_free(ctx);

    return 0;
}

static int server_socket = -1;
int server_tcp::process()
{
   uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
    int master_socket;
    int rc;
    fd_set refset;
    fd_set rdset;
    /* Maximum file descriptor number */
    int fdmax;
    modbus_t *ctx;
    modbus_mapping_t *mb_mapping;

    ctx = modbus_new_tcp("0.0.0.0", 1502);
    printf("tcp listen port:1502\n");
    server_socket = modbus_tcp_listen(ctx, NB_CONNECTION);
    if (server_socket == -1) {
        fprintf(stderr, "Unable to listen TCP connection\n");
        modbus_free(ctx);
        return -1;
    }

    //signal(SIGINT, close_sigint);

    /* Clear the reference set of socket */
    FD_ZERO(&refset);
    /* Add the server socket */
    FD_SET(server_socket, &refset);

    /* Keep track of the max file descriptor */
    fdmax = server_socket;

    for (;;) {
        rdset = refset;
        if (select(fdmax + 1, &rdset, NULL, NULL, NULL) == -1) {
            perror("Server select() failure.");
            //close_sigint(1);
        }

        /* Run through the existing connections looking for data to be
         * read */
        for (master_socket = 0; master_socket <= fdmax; master_socket++) {

            if (!FD_ISSET(master_socket, &rdset)) {
                continue;
            }

            if (master_socket == server_socket) {
                /* A client is asking a new connection */
                socklen_t addrlen;
                struct sockaddr_in clientaddr;
                int newfd;

                /* Handle new connections */
                addrlen = sizeof(clientaddr);
                memset(&clientaddr, 0, sizeof(clientaddr));
                newfd = accept(server_socket, (struct sockaddr *) &clientaddr, &addrlen);
                if (newfd == -1) {
                    perror("Server accept() error");
                } else {
                    FD_SET(newfd, &refset);

                    if (newfd > fdmax) {
                        /* Keep track of the maximum */
                        fdmax = newfd;
                    }
                    printf("New connection from %s:%d on socket %d\n",
                           inet_ntoa(clientaddr.sin_addr),
                           clientaddr.sin_port,
                           newfd);
                }
            } else {
                modbus_set_socket(ctx, master_socket);
                rc = modbus_receive(ctx, query);
                if (rc > 0) {
                                /* rc is the query size */
                    unsigned int  offset = ctx->backend->header_length;
                    int slaveid = query[offset-1];
                    mb_mapping = mbserver_mgr::getInstance()->get_regmap(slaveid);
                    if (mb_mapping == NULL) {
                        fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno));
                        //modbus_free(ctx);
                        //return -1;
                        continue;
                    }
                    modbus_reply(ctx, query, rc, mb_mapping);
                } else if (rc == -1) {
                    /* This example server in ended on connection closing or
                     * any errors. */
                    printf("Connection closed on socket %d\n", master_socket);
                    close(master_socket);

                    /* Remove from reference set */
                    FD_CLR(master_socket, &refset);

                    if (master_socket == fdmax) {
                        fdmax--;
                    }
                }
            }
        }
    }

    return 0;
}

//================

int server_rtu::init()
{
    //ctx = modbus_new_rtu(ip_or_device, 115200, 'N', 8, 1);
    //modbus_set_slave(ctx, SERVER_ID);
    //query = malloc(MODBUS_RTU_MAX_ADU_LENGTH);
    std::thread t = std::thread([=](){
        this->process();
    });
    t.detach();
}

int server_rtu::process()
{
    int s = -1;
    int SERVER_ID = 1;
    modbus_t *ctx;
    modbus_mapping_t *mb_mapping;

    ctx = modbus_new_rtu("/dev/uart1", 115200, 'N', 8, 1);
    modbus_set_slave(ctx, SERVER_ID);
    /* modbus_set_debug(ctx, TRUE); */

    int rc1 = modbus_connect(ctx);
    if (rc1 == -1) {
        fprintf(stderr, "Unable to connect %s\n", modbus_strerror(errno));
        modbus_free(ctx);
        return -1;
    }

    for (;;) {
        uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
        int rc;

        rc = modbus_receive(ctx, query);
        if (rc > 0) {
            /* rc is the query size */
            unsigned int  offset = ctx->backend->header_length;
            int slaveid = query[offset-1];
            mb_mapping = mbserver_mgr::getInstance()->get_regmap(slaveid);
            if (mb_mapping == NULL) {
                fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno));
                //modbus_free(ctx);
                //return -1;
                continue;
            }

            modbus_reply(ctx, query, rc, mb_mapping);
        } else if (rc == -1) {
            /* Connection closed by the client or error */
            break;
        }
    }

    printf("Quit the loop: %s\n", modbus_strerror(errno));

    if (s != -1) {
        close(s);
    }
    //modbus_mapping_free(mb_mapping);
    modbus_close(ctx);
    modbus_free(ctx);

    return 0;   
}

//==================

//=====================

shared_ptr<mbserver_mgr>     mbserver_mgr::pdevicemgr;
shared_ptr<mbserver_mgr> mbserver_mgr::getInstance()
{
    if(nullptr == pdevicemgr)
    {
        pdevicemgr.reset(new mbserver_mgr());
    }
    return pdevicemgr;
}
void mbserver_mgr::init_dpsset_fromchannelmgr(vector<dp_param_t>dps)
{
    m_dpset.assign(dps.begin(),dps.end());
}

void mbserver_mgr::init()
{
    //初始化寄存器列表
    dp_param_t t1={.groupId =1, .slaveId =1, .fc_code =4, .regStart =30001, .regCount =1, .regValue=0x1234, .isMapped =0};
    dp_param_t t3={.groupId =1, .slaveId =1, .fc_code =4, .regStart =30002, .regCount =1, .regValue=0x4567, .isMapped =0};
    dp_param_t t2={.groupId =1, .slaveId =1, .fc_code =3, .regStart =40005, .regCount =1, .regValue=0x9988, .isMapped =0};
    //模拟气体检测器
    dp_param_t gas1={.groupId =1, .slaveId =2, .fc_code =3, .regStart =0x0100, .regCount =1, .regValue=0x1234, .isMapped =0};
    dp_param_t gas2={.groupId =1, .slaveId =2, .fc_code =3, .regStart =0x0101, .regCount =1, .regValue=0x4567, .isMapped =0};
    dp_param_t gas3={.groupId =1, .slaveId =2, .fc_code =3, .regStart =0x0102, .regCount =1, .regValue=0x9988, .isMapped =0};
    dp_param_t gas4={.groupId =1, .slaveId =2, .fc_code =3, .regStart =0x0103, .regCount =1, .regValue=0x1234, .isMapped =0};
    dp_param_t gas5={.groupId =1, .slaveId =2, .fc_code =3, .regStart =0x0104, .regCount =1, .regValue=0x4567, .isMapped =0};
    dp_param_t gas6={.groupId =1, .slaveId =2, .fc_code =3, .regStart =0x0105, .regCount =1, .regValue=0x9988, .isMapped =0};
    dp_param_t gas7={.groupId =1, .slaveId =2, .fc_code =3, .regStart =0x0106, .regCount =1, .regValue=0x1234, .isMapped =0};
    dp_param_t gas8={.groupId =1, .slaveId =2, .fc_code =3, .regStart =0x0107, .regCount =1, .regValue=0x4567, .isMapped =0};
    dp_param_t gas9={.groupId =1, .slaveId =2, .fc_code =3, .regStart =0x0108, .regCount =1, .regValue=0x9988, .isMapped =0};

    m_dpset.push_back(t1);
    m_dpset.push_back(t2);
    m_dpset.push_back(t3);
    m_dpset.push_back(gas1);
    m_dpset.push_back(gas2);
    m_dpset.push_back(gas3);
    m_dpset.push_back(gas4);
    m_dpset.push_back(gas5);
    m_dpset.push_back(gas6);
    m_dpset.push_back(gas7);
    m_dpset.push_back(gas8);
    m_dpset.push_back(gas9);

    sort_dp();
    m_mb_tcpserver.reset(new server_tcp()) ;
    m_mb_rtuserver.reset(new server_rtu()) ;


    m_moduleId  = moduleid(EMODULE_MODULE_MBSERVER_BASE,1); 
    CModule_Mgr::getInstance()->reg_module(m_moduleId, pdevicemgr);
}
void mbserver_mgr::start()
{
    //初始化mapp
    m_mb_tcpserver->init();
    m_mb_rtuserver->init();
    CModule::start();

    CMsg_Ptr pMsg(new msg_start());
    this->put_msg(pMsg);
}
void mbserver_mgr::handle_msg(CMsg_Ptr pMsg)
{
    switch(pMsg->m_type)
    {
        case EMSG_SYS_START:
        {
            m_loopWkTm.add_task(mstm(DF_LOOP_TM_MS),[=](){
                CMsg_Ptr pmsg(new msg_loop());
                this->put_msg(pmsg);
            }, 1);
            return;
        }
        break;
        case EMSG_LOOP:
        {
            //do something

            //循环定时器
            m_loopWkTm.add_task(mstm(DF_LOOP_TM_MS),[=](){
                CMsg_Ptr pmsg(new msg_loop());
                this->put_msg(pmsg);
            }, 1);
        }
        break;
        case EMSG_DP_UPDATE:
        {
            msg_update_ptr p = dynamic_pointer_cast<msg_update_value>(pMsg);
            handle_updatedp(p->m_dpparam);
        }
        break;
    }
}

void mbserver_mgr::handle_updatedp(dp_param_t& dpparam)
{
    std::lock_guard<std::mutex> guard(m_mapLock);
    //找到到对应的位置，更新值
    if(m_regmaps.find(dpparam.slaveId)!=m_regmaps.end())
    {
        modbus_mapping_t* p_mbmap = m_regmaps.at(dpparam.slaveId);
        switch(dpparam.fc_code )
            {
                case  MODBUS_FC_READ_COILS: 
                {
                    if(p_mbmap->nb_bits>dpparam.regStart -p_mbmap->start_bits)
                        p_mbmap->tab_bits[dpparam.regStart -p_mbmap->start_bits ] = dpparam.regValue;
                    else{
                        printf(" MODBUS_FC_READ_COILS over max,map update failed\n");
                    }
                }
                break;
                case MODBUS_FC_READ_DISCRETE_INPUTS:
                {
                    if(p_mbmap->nb_input_bits > dpparam.regStart -p_mbmap->start_input_bits)
                        p_mbmap->tab_input_bits[dpparam.regStart -p_mbmap->start_input_bits ] = dpparam.regValue;
                    else{
                        printf(" MODBUS_FC_READ_DISCRETE_INPUTS over max,map update failed\n");
                    }
                }
                break;
                case MODBUS_FC_READ_HOLDING_REGISTERS:
                {
                    if(p_mbmap->nb_registers > dpparam.regStart -p_mbmap->start_registers)
                        p_mbmap->tab_registers[dpparam.regStart -p_mbmap->start_registers ] = dpparam.regValue;
                    else{
                        printf(" MODBUS_FC_READ_HOLDING_REGISTERS over max,map update failed\n");
                    }
                }
                break;
                case MODBUS_FC_READ_INPUT_REGISTERS:
                {
                    if(p_mbmap->nb_input_registers > dpparam.regStart -p_mbmap->start_input_registers)
                        p_mbmap->tab_input_registers[dpparam.regStart -p_mbmap->start_input_registers ] = dpparam.regValue;
                    else{
                        printf(" MODBUS_FC_READ_INPUT_REGISTERS over max,map update failed\n");
                    }
                }
                break;
            }
    }
    else
    {
        printf("can not find slaveID map\n");
    }
}
void mbserver_mgr::sort_dp()
{
    std::lock_guard<std::mutex> guard(m_mapLock);
    //通过slaveid  进行分组
    map<int,vector<dp_param_t>>temp;
    for(auto it:m_dpset)
    {
        if(temp.find(it.slaveId)==temp.end())
        {
            temp[it.slaveId] = {it};
        }
        else{
            temp[it.slaveId].push_back(it);
        }
    }
    //排序
    for(auto&it:temp)
    {
        sort(it.second.begin(),it.second.end(),[](const dp_param_t& a,const dp_param_t& b){
            return a.regStart < b.regStart;
        });
        //创建map
        modbus_mapping_t *mb_mapping;
        mb_mapping = modbus_mapping_new(500, 500, 500, 500);
        //给map赋值
        for(auto& dp:it.second)
        {
            switch(dp.fc_code )
            {
                case  MODBUS_FC_READ_COILS: 
                {
                    if(mb_mapping->start_bits==0)
                    {
                        mb_mapping->start_bits = dp.regStart;
                    }
                    mb_mapping->tab_bits[dp.regStart -mb_mapping->start_bits ] = dp.regValue;
                    printf("mappig:1 reg:%d==>slaveid:%d,func:%d,start:%d,offset:%d\n",dp.regStart,dp.slaveId,dp.fc_code,mb_mapping->start_bits,dp.regStart -mb_mapping->start_bits);
                }
                break;
                case MODBUS_FC_READ_DISCRETE_INPUTS:
                {
                    if(mb_mapping->start_input_bits==0)
                    {
                        mb_mapping->start_input_bits = dp.regStart;
                    }
                    mb_mapping->tab_input_bits[dp.regStart -mb_mapping->start_input_bits ] = dp.regValue;
                    printf("mappig:2 reg:%d==>slaveid:%d,func:%d,start:%d,offset:%d\n",dp.regStart,dp.slaveId,dp.fc_code,mb_mapping->start_input_bits,dp.regStart -mb_mapping->start_input_bits);
                }
                break;
                case MODBUS_FC_READ_HOLDING_REGISTERS:
                {
                    if(mb_mapping->start_registers==0)
                    {
                        mb_mapping->start_registers = dp.regStart;
                    }
                    mb_mapping->tab_registers[dp.regStart -mb_mapping->start_registers ] = dp.regValue;
                    printf("mappig:3 reg:%d==>slaveid:%d,func:%d,start:%d,offset:%d\n",dp.regStart,dp.slaveId,dp.fc_code,mb_mapping->start_registers,dp.regStart -mb_mapping->start_registers);

                }
                break;
                case MODBUS_FC_READ_INPUT_REGISTERS:
                {
                    if(mb_mapping->start_input_registers==0)
                    {
                        mb_mapping->start_input_registers = dp.regStart;
                    }
                    mb_mapping->tab_input_registers[dp.regStart -mb_mapping->start_input_registers ] = dp.regValue;
                    printf("mappig:4 reg:%d==>slaveid:%d,func:%d,start:%d,offset:%d\n",dp.regStart,dp.slaveId,dp.fc_code,mb_mapping->start_input_registers,dp.regStart -mb_mapping->start_input_registers);

                }
                break;
            }

        }
        m_regmaps[it.first] =mb_mapping;
        
    }

    
}
modbus_mapping_t* mbserver_mgr::get_regmap(int slaveId)
{
    std::lock_guard<std::mutex> guard(m_mapLock);
    if(m_regmaps.find(slaveId)!=m_regmaps.end())
    {
        return m_regmaps[slaveId];
    }
    return nullptr;
}
